package com.jahe.learn.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 简单的䃧室服务器端。
 * 我的消息发给除了自己的所有人。
 * @author: jahe.lai
 * @create: 2019-12-25 14:37
 */
public class NioServer43 {

    private static Map<String, SocketChannel> channelMap = new ConcurrentHashMap();

    public static void main(String[] args) throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        ServerSocket serverSocket = serverSocketChannel.socket();
        serverSocket.setReuseAddress(true);
        InetSocketAddress address = new InetSocketAddress(8899);
        serverSocket.bind(address);

        Selector selector = Selector.open();
        // 注册，实现通道与选择器的关联关系
        // SelectionKey是一个token，代表了一个channel注册到一个Selector上的关系。
        SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        while (true) {
            try {
                int select = selector.select();
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                selectionKeys.forEach(skey -> {
                    final SocketChannel clientConn;
                    try {
                        if (skey.isAcceptable()) {
                            // 正在监听连接的channel
                            ServerSocketChannel ssChannel = (ServerSocketChannel) skey.channel();
                            // 新建立连接的channel
                            clientConn = ssChannel.accept();
                            // 将新连接的channel设置为非阻塞
                            clientConn.configureBlocking(false);

                            // 注册到selector 上，并添加关注的事件。
                            clientConn.register(selector, SelectionKey.OP_READ);
                            channelMap.put("【" + UUID.randomUUID().toString() + "】", clientConn);
                        }  else if (skey.isReadable()) {
                            clientConn = (SocketChannel) skey.channel();
                            ByteBuffer readBuffer = ByteBuffer.allocate(512);
                            readBuffer.clear();
                            int len = clientConn.read(readBuffer);
                            if (len > 0){
                                readBuffer.flip();
                                String msg = new String(readBuffer.array(), "utf-8");
                                System.out.println("msg ===" + msg);
                                for (Map.Entry<String, SocketChannel> entry : channelMap.entrySet()) {
                                    if (clientConn == entry.getValue()) {
                                        continue;
                                    }
                                    ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
                                    writeBuffer.put((entry.getKey() + " : " + msg).getBytes());
                                    writeBuffer.flip();
                                    entry.getValue().write(writeBuffer);
                                }
                            }
                        }
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                });
                selectionKeys.clear(); // 一定要清空，否则会报异常。
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }
}
